package cn.crawler.app.crown.modules.services.impl;

import cn.crawler.app.crown.modules.models.CrownConfig;
import cn.crawler.app.crown.modules.models.CrownMatch;
import cn.crawler.app.crown.modules.models.constant.CrownConstant;
import cn.crawler.app.crown.modules.models.constant.Fields;
import cn.crawler.app.crown.modules.models.enums.MatchStageEnum;
import cn.crawler.app.crown.modules.services.CrownBetService;
import cn.crawler.app.crown.modules.services.CrownConfigService;
import cn.crawler.app.crown.modules.services.CrownMatchService;
import cn.crawler.app.crown.modules.services.result.MatchResultService;
import cn.crawler.app.sys.modules.services.SysParamService;
import cn.crawler.framework.base.model.enums.BizExceptionEnum;
import cn.crawler.framework.base.service.BaseServiceImpl;
import cn.crawler.framework.base.service.CommonDBService;
import cn.crawler.framework.page.Pagination;
import cn.crawler.framework.util.Result;
import cn.crawler.app.crown.commons.util.DateUtil;
import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Times;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = CrownMatchService.class)
public class CrownMatchServiceImpl extends BaseServiceImpl<CrownMatch> implements CrownMatchService {

    public CrownMatchServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private RedisService redisService;
    @Inject
    private CrownBetService betService;
    @Inject
    private CommonDBService dbService;
    @Inject
    private SysParamService sysParamService;
    @Inject
    @Reference(retries = 3, timeout = 500000)
    private CrownConfigService crownConfigService;
    @Inject
    @Reference(retries = 3, timeout = 500000)
    private MatchResultService ftMatchResultService;

    private static final Log log = Logs.get();

    /**
     * 匹配年月日 2020年1月1日
     */
    private static final Pattern Y_M_D = Pattern.compile("(20\\d{2}[年]\\d{1,2}[月]\\d{0,2}[日])");
    /**
     * 匹配过期具体时间 ~17:00:00)
     */
    private static final Pattern H_M_S = Pattern.compile("~([\\s\\S]*?)\\(");

    @Override
    public CrownMatch queryByTeamInfo(String league, String home, String custom, Date dateTime) {
        Cnd cnd = Cnd.where("league", "=", league).and("home", "=", home)
                .and("custom", "=", custom).and("dateTime", "=", dateTime);
        return this.fetch(cnd);
    }

    @Override
    public Pagination leagueList(MatchStageEnum stage, int pageNo, int pageSize) {
        // 根据不同阶段查询未开赛的赛事列表
        Cnd cnd = Cnd.where("dateTime", ">", Times.getNowSDT());
        if (null != stage) {
            cnd.and("stage", "=", stage);
        }
        List<CrownMatch> list = this.query(cnd);
        // 获取赛事的联赛名集合(去重)
        List<String> leagueList = list.stream().map(CrownMatch::getLeague).distinct().collect(Collectors.toList());
        return this.getPagination(pageNo, pageSize, leagueList);
    }

    @Override
    public Pagination matchList(MatchStageEnum stage, List<String> leagueList, int pageNo, int pageSize) {
        List<CrownMatch> list = this.getCrownMatches(stage);
        // 筛选、排序
        Stream<CrownMatch> stream = list.stream();
        // 按联赛筛选
        if (CollectionUtils.isNotEmpty(leagueList)) {
            stream = stream.filter(match -> leagueList.contains(match.getLeague()));
        }
        //分页
        list = stream.collect(Collectors.toList());
        return this.getPagination(pageNo, pageSize, list);
    }

    private List<CrownMatch> getCrownMatches(MatchStageEnum stage) {
        // 系统当前时间转为美东时间
        String nowStr = DateUtil.timeZoneConvertEdt(Times.now());
        // 系统后日零点转为美东时间
        String afterZeroStr = DateUtil.timeZoneConvertEdt(Times.ts2D(DateUtil.getZeroLong(+2)));
        // 系统明日零点转为美东时间
        long tomorrowZero = Times.ams(DateUtil.timeZoneConvertEdt(Times.ts2D(DateUtil.getZeroLong(+1)))) / 1000;
        // 查出当前时间之后，后天零点之前的所有赛事
        List<CrownMatch> list = this.query(Cnd.where("dateTime", ">", nowStr).and("dateTime", "<", afterZeroStr).asc("dateTime"));
        // today
        if (MatchStageEnum.BROWSE.equals(stage)) {
            // 当stage是今日，保留明天零点之前的比赛
            list = list.stream().filter(match -> Times.d2TS(match.getDateTime()) < tomorrowZero).collect(Collectors.toList());
        }
        // future
        else if (MatchStageEnum.FUTURE.equals(stage)) {
            // 当stage是明日，保留明天零点之后的比赛
            list = list.stream().filter(match -> Times.d2TS(match.getDateTime()) >= tomorrowZero).collect(Collectors.toList());
        }
        return list;
    }


    /**
     * 分页方法抽取
     *
     * @param pageNo   页码
     * @param pageSize 页面容量
     * @param list     数据集合
     * @return Pagination
     */
    private Pagination getPagination(int pageNo, int pageSize, List list) {
        int endIndex = pageNo == 0 ? pageSize : pageNo * pageSize;
        int count = list.size();
        int startIndex = endIndex - pageSize;
        list = list.subList(pageNo == 0 ? 0 : startIndex, endIndex == 0 ? count : Math.min(endIndex, count));
        // 设置分页属性
        Pagination pagination = new Pagination();
        pagination.setTotalCount(count);
        pagination.setPageNo(pageNo);
        pagination.setPageSize(pageSize);
        pagination.setList(list);
        return pagination;
    }



    @Override
    public Pagination resultList(Long start, Long end, int pageNo, int pageSize) {
        Sql sql = Sqls.create("select * from crown_match $condition ");
        Sql countSql = Sqls.create("select count(1) from crown_match $condition ");
        Cnd cnd = Cnd.where(Cnd.exps("stage", "=", CrownConstant.FINISHED)
                .or("stage", "=", CrownConstant.OTHER));
        // 如果传了时间，按时间区间查询
        if (null != start && null != end) {
            String startTime = Times.sDT(new Date(start * 1000));
            String endTime = Times.sDT(new Date(end * 1000));
            cnd.and("dateTime", ">=", startTime).and("dateTime", "<=", endTime);
        }
        sql.setCondition(cnd.desc("dateTime"));
        countSql.setCondition(cnd.desc("dateTime"));
        return this.listPage(pageNo, pageSize, sql, countSql);
    }


    @Override
    public CrownMatch result(String id) {
        return this.fetch(id);
    }


    @Override
    public void crawlMatchResult(Long startTime, Long endTime) {
        // 获取爬虫账号
        CrownConfig crownConfig = crownConfigService.syncOne("手动获取赛果任务");
        if (crownConfig != null) {
            log.info("准备开始任务:" + crownConfig);
            try {
                // 一天的毫秒值
                long oneDayMilliSecond = 24 * 60 * 60 * 1000;
                // 计算要爬取的天数
                long dayNum = (endTime - startTime) / oneDayMilliSecond + 1;
                // 如果不限制天数爬虫程序可能会超时
                if (dayNum > 2) {
                    Result.breakWith(BizExceptionEnum.CHOOSE_ONE_DAY_TO_CRAWLING);
                }
                // 处理日期格式
                String start = DateFormatUtils.format(startTime, "yyyy-MM-dd HH:mm:ss");
                String end = DateFormatUtils.format(endTime, "yyyy-MM-dd HH:mm:ss");
                // 查询日期范围内所有状态不是finished的赛事
                Cnd cnd = Cnd.NEW();
                cnd.where().andBetween("dateTime", start, end)
                        .and("stage", "!=", "finished").and("stage", "!=", "other");
                List<CrownMatch> list = this.query(cnd);
                for (int i = 0; i < list.size(); i++) {
                    // 爬足球赛果
                    ftMatchResultService.crawler(list.get(i), crownConfig);
                }
            } finally {
                // 获取账号，判断是否为空，以防账号已经被清除
                crownConfigService.release(crownConfig);
            }
        } else {
            Result.breakWith(BizExceptionEnum.NO_ACCOUNT);
        }

    }



    @Override
    public List matchCount() {
        List<CrownMatch> browses = this.getCrownMatches(MatchStageEnum.BROWSE);
        List<CrownMatch> futures = this.getCrownMatches(MatchStageEnum.FUTURE);

        // 过滤后集合size是BROWSE的数量
        int broseCount = browses.size();
        int futureCount = futures.size();

        List<Map> result = new ArrayList<>();
        result.add(
                Result.createMap(
                        Fields.MATCH_COUNT_FIELD_STAGE, MatchStageEnum.BROWSE,
                        Fields.MATCH_COUNT_FIELD_COUNT, broseCount
                )
        );
        result.add(
                Result.createMap(
                        Fields.MATCH_COUNT_FIELD_STAGE, MatchStageEnum.FUTURE,
                        Fields.MATCH_COUNT_FIELD_COUNT, futureCount
                )
        );
        result.add(
                Result.createMap(
                        Fields.MATCH_COUNT_FIELD_STAGE, "All",
                        Fields.MATCH_COUNT_FIELD_COUNT, futureCount + broseCount
                )
        );
        return result;
    }
}
